﻿using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace SZHelperCore
{
    public static class Security
    {
        private static string _QueryStringKey = "sydzl521"; // 8-byte key for DES
        private static string _LongQueryStringKey = ",sydzl521,./!@#$%^&*())("; // 24-byte key for 3DES

        public static string QueryStringKey
        {
            get => _QueryStringKey;
            set => _QueryStringKey = value ?? throw new ArgumentNullException(nameof(value));
        }

        public static string LongQueryStringKey
        {
            get => _LongQueryStringKey;
            set => _LongQueryStringKey = value ?? throw new ArgumentNullException(nameof(value));
        }

        // =============== DES (8-byte key) ===============

        public static string EncryptQueryString(this string query)
            => query.Encrypt(_QueryStringKey);

        public static string DecryptQueryString(this string query)
            => query.Decrypt(_QueryStringKey);

        public static string Encrypt(this string plainText, string key)
        {
            if (string.IsNullOrEmpty(plainText)) return string.Empty;
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("密钥不能为空", nameof(key));

            byte[] keyBytes = Encoding.ASCII.GetBytes(key).Take(8).ToArray(); // DES requires 8 bytes
            byte[] iv = keyBytes; // Use key as IV (insecure but matches legacy behavior)

            using var des = DES.Create();
            des.Key = keyBytes;
            des.IV = iv;
            des.Mode = CipherMode.CBC;
            des.Padding = PaddingMode.PKCS7;

            byte[] input = Encoding.UTF8.GetBytes(plainText); // ✅ UTF-8 instead of Default

            using var ms = new MemoryStream();
            using var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(input, 0, input.Length);
            cs.FlushFinalBlock();

            return Convert.ToHexString(ms.ToArray()); // Hex uppercase, no "-"
        }

        public static string Decrypt(this string hexCipherText, string key)
        {
            if (string.IsNullOrEmpty(hexCipherText)) return string.Empty;
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("密钥不能为空", nameof(key));

            if (hexCipherText.Length % 2 != 0)
                throw new ArgumentException("密文必须是偶数长度的十六进制字符串", nameof(hexCipherText));

            byte[] cipherBytes = Enumerable.Range(0, hexCipherText.Length / 2)
                .Select(i => Convert.ToByte(hexCipherText.Substring(i * 2, 2), 16))
                .ToArray();

            byte[] keyBytes = Encoding.ASCII.GetBytes(key).Take(8).ToArray();
            byte[] iv = keyBytes;

            using var des = DES.Create();
            des.Key = keyBytes;
            des.IV = iv;
            des.Mode = CipherMode.CBC;
            des.Padding = PaddingMode.PKCS7;

            using var ms = new MemoryStream();
            using var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(cipherBytes, 0, cipherBytes.Length);
            cs.FlushFinalBlock();

            return Encoding.UTF8.GetString(ms.ToArray());
        }

        // =============== TripleDES (24-byte key) ===============

        public static string LongEncryptQueryString(this string query)
            => query.LongEncrypt(_LongQueryStringKey);

        public static string LongDecryptQueryString(this string query)
            => query.LongDecrypt(_LongQueryStringKey);

        public static string LongEncrypt(this string plainText, string key)
        {
            if (string.IsNullOrEmpty(plainText)) return string.Empty;
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("密钥不能为空", nameof(key));

            byte[] keyBytes = Encoding.ASCII.GetBytes(key).Take(24).ToArray(); // 3DES: 24 bytes
            // ECB mode has no IV — matches legacy

            using var tdes = TripleDES.Create();
            tdes.Key = keyBytes;
            tdes.Mode = CipherMode.ECB; // ⚠️ Insecure, but kept for compatibility
            tdes.Padding = PaddingMode.PKCS7;

            byte[] input = Encoding.ASCII.GetBytes(plainText); // Legacy uses ASCII

            byte[] encrypted = tdes.CreateEncryptor().TransformFinalBlock(input, 0, input.Length);
            return Convert.ToBase64String(encrypted);
        }

        public static string LongDecrypt(this string base64CipherText, string key)
        {
            if (string.IsNullOrEmpty(base64CipherText)) return string.Empty;
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("密钥不能为空", nameof(key));

            byte[] cipherBytes = Convert.FromBase64String(base64CipherText);
            byte[] keyBytes = Encoding.ASCII.GetBytes(key).Take(24).ToArray();

            using var tdes = TripleDES.Create();
            tdes.Key = keyBytes;
            tdes.Mode = CipherMode.ECB;
            tdes.Padding = PaddingMode.PKCS7;

            try
            {
                byte[] decrypted = tdes.CreateDecryptor().TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
                return Encoding.ASCII.GetString(decrypted);
            }
            catch (CryptographicException)
            {
                // Match legacy behavior: return empty on failure
                return string.Empty;
            }
        }

        // =============== Utility Methods ===============

        public static bool ValidateString(this string encryptedString, string originalString)
        {
            try
            {
                return encryptedString.Decrypt(_QueryStringKey) == originalString;
            }
            catch
            {
                return false;
            }
        }

        // ========== AES 安全加密（.NET 8+ 推荐方式）==========
        private const int SALT_SIZE = 16;      // 128 bits
        private const int KEY_SIZE = 32;       // 256 bits for AES-256
        private const int IV_SIZE = 16;        // AES block size
        private const int ITERATIONS = 100_000;

        /// <summary>
        /// 使用 AES-256 + PBKDF2 安全加密字符串
        /// </summary>
        public static string AesEncrypt(this string plainText, string password)
        {
            if (string.IsNullOrEmpty(plainText)) return string.Empty;
            if (string.IsNullOrEmpty(password))
                throw new ArgumentException("密码不能为空", nameof(password));

            byte[] salt = RandomNumberGenerator.GetBytes(SALT_SIZE);
            byte[] iv = RandomNumberGenerator.GetBytes(IV_SIZE);

            // ✅ 使用静态 Pbkdf2 方法（.NET 8+ 推荐）
            byte[] key = Rfc2898DeriveBytes.Pbkdf2(
                password,
                salt,
                ITERATIONS,
                HashAlgorithmName.SHA256,
                KEY_SIZE
            );

            using var aes = Aes.Create();
            aes.Key = key;
            aes.IV = iv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
            byte[] cipherBytes = aes.CreateEncryptor().TransformFinalBlock(plainBytes, 0, plainBytes.Length);

            using var ms = new MemoryStream();
            ms.Write(salt);
            ms.Write(iv);
            ms.Write(cipherBytes);
            return Convert.ToBase64String(ms.ToArray());
        }

        /// <summary>
        /// 使用 AES-256 + PBKDF2 安全解密字符串
        /// </summary>
        public static string AesDecrypt(this string base64CipherText, string password)
        {
            if (string.IsNullOrEmpty(base64CipherText)) return string.Empty;
            if (string.IsNullOrEmpty(password))
                throw new ArgumentException("密码不能为空", nameof(password));

            byte[] data;
            try
            {
                data = Convert.FromBase64String(base64CipherText);
            }
            catch (FormatException)
            {
                throw new CryptographicException("无效的 Base64 密文");
            }

            if (data.Length < SALT_SIZE + IV_SIZE)
                throw new CryptographicException("密文太短");

            ReadOnlySpan<byte> span = data;
            byte[] salt = span[..SALT_SIZE].ToArray();
            byte[] iv = span[SALT_SIZE..(SALT_SIZE + IV_SIZE)].ToArray();
            byte[] cipherBytes = span[(SALT_SIZE + IV_SIZE)..].ToArray();

            // ✅ 同样使用静态 Pbkdf2 方法
            byte[] key = Rfc2898DeriveBytes.Pbkdf2(
                password,
                salt,
                ITERATIONS,
                HashAlgorithmName.SHA256,
                KEY_SIZE
            );

            using var aes = Aes.Create();
            aes.Key = key;
            aes.IV = iv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            try
            {
                byte[] plainBytes = aes.CreateDecryptor().TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
                return Encoding.UTF8.GetString(plainBytes);
            }
            catch (CryptographicException ex)
            {
                throw new CryptographicException("解密失败：密码错误或数据已损坏", ex);
            }
        }

        // ========== 便捷方法（使用默认密码）==========

        private static string _DefaultAesPassword = "ChangeMeToAStrongPassword!2025";

        public static string DefaultAesPassword
        {
            get => _DefaultAesPassword;
            set => _DefaultAesPassword = value ?? throw new ArgumentNullException(nameof(value));
        }

        public static string AesEncryptQueryString(this string query)
            => query.AesEncrypt(_DefaultAesPassword);

        public static string AesDecryptQueryString(this string query)
            => query.AesDecrypt(_DefaultAesPassword);

        public static bool ValidateAesString(this string encrypted, string original)
        {
            try
            {
                return encrypted.AesDecrypt(_DefaultAesPassword) == original;
            }
            catch
            {
                return false;
            }
        }

        public static string Base64Code(this string message)
        {
            if (string.IsNullOrEmpty(message)) return string.Empty;
            byte[] bytes = Encoding.UTF8.GetBytes(message); // ✅ UTF-8
            return Convert.ToBase64String(bytes);
        }

        public static string Base64Decode(string message)
        {
            if (string.IsNullOrEmpty(message)) return string.Empty;
            try
            {
                byte[] bytes = Convert.FromBase64String(message);
                return Encoding.UTF8.GetString(bytes); // ✅ UTF-8
            }
            catch (FormatException)
            {
                return string.Empty; // Match legacy silent fail
            }
        }
    }
}